package com.hua.giftool.utils

import android.content.ContentValues
import android.content.Context
import android.media.MediaMetadataRetriever
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.FFmpegKitConfig
import com.arthenica.ffmpegkit.ReturnCode
import com.arthenica.ffmpegkit.Session
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.util.Locale
import kotlin.math.log10
import kotlin.math.min
import kotlin.math.pow

object GifConstants {
    const val DEFAULT_FPS = 30
    const val DEFAULT_WIDTH = 300
    const val DEFAULT_HEIGHT = -1
    const val DEFAULT_OUTPUT_DIR = "ShaoMaoGif"
    const val DEFAULT_LENS =
        "flags=lanczos,split[s0][s1];[s0]palettegen=stats_mode=full[p];[s1][p]paletteuse=dither=sierra2_4a"
}

/**
 * 从Uri获取文件路径
 */
fun <T : Context> T.getPathFromUri(uri: Uri): String? {
    var path: String? = null
    val projection = arrayOf(MediaStore.Images.Media.DATA)
    val cursor = contentResolver.query(uri, projection, null, null, null)
    cursor?.use {
        if (it.moveToFirst()) {
            val columnIndex = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
            path = it.getString(columnIndex)
        }
    }
    return path
}

/**
 * 从Uri获取视频时长
 */
fun <T : Context> T.getDurationFromUri(uri: Uri): Long {
    val retriever = MediaMetadataRetriever()
    try {
        retriever.setDataSource(this, uri)
        val duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
        return duration?.toLongOrNull() ?: 0
    } catch (e: Exception) {
        e.printStackTrace()
        return 0
    } finally {
        retriever.release()
    }
}

/**
 * 估算GIF文件大小
 * @param videoUri 视频Uri
 * @param targetFps 目标帧率
 * @param targetWidth 目标宽度
 * @param targetHeight 目标高度
 * @param originalDurationMs 原始视频时长
 */
fun <T : Context> T.estimateGifFileSize(
    videoUri: Uri?,
    targetFps: Int,
    targetWidth: Int,
    targetHeight: Int,
    originalDurationMs: Long
): String {
    if (videoUri == null || originalDurationMs <= 0 || targetFps <= 0 || targetWidth <= 0 && targetHeight <= 0) {
        return "" // 信息不完整，无法预估
    }

    // 粗略估算：假设每帧数据量与像素数量成正比，并考虑一些压缩因子
    // 这是一个非常简化的模型，实际情况复杂得多
    val estimatedFrames = (originalDurationMs / 1000.0 * targetFps).toInt()
    val estimatedPixelsPerFrame = if (targetWidth > 0 && targetHeight > 0) {
        targetWidth * targetHeight
    } else if (targetWidth > 0) {
        // 假设保持比例，需要获取原始视频宽高
        val originalSize = getVideoDimensionsFromUri(videoUri)
        if (originalSize.first > 0 && originalSize.second > 0) {
            (targetWidth * (originalSize.second.toFloat() / originalSize.first)).toInt() * targetWidth
        } else {
            targetWidth * GifConstants.DEFAULT_WIDTH // 无法获取原始比例，使用一个默认高度进行估算
        }
    } else {
        // 假设保持比例，需要获取原始视频宽高
        val originalSize = getVideoDimensionsFromUri(videoUri)
        if (originalSize.first > 0 && originalSize.second > 0) {
            (targetHeight * (originalSize.first.toFloat() / originalSize.second)).toInt() * targetHeight
        } else {
            targetHeight * GifConstants.DEFAULT_WIDTH // 无法获取原始比例，使用一个默认宽度进行估算
        }
    }

    // 估算每像素的字节数 (GIF 通常使用 256 色调色板，每个像素可能需要 1 字节或更多用于颜色索引和控制信息)
    // 这里的 bytesPerPixel 是一个非常粗略的经验系数，需要根据实际测试调整
    val bytesPerPixel = 0.68

    val estimatedTotalBytes = estimatedFrames * estimatedPixelsPerFrame * bytesPerPixel

    // 将字节转换为更易读的格式 (KB, MB)
    return formatBytes(estimatedTotalBytes.toLong())
}

/**
 * 从Uri获取视频尺寸
 * @param uri 视频Uri
 * @return Pair<Int, Int>，第一个元素是宽度，第二个元素是高度
 * 如果无法获取尺寸，则返回Pair(0, 0)
 */
fun <T : Context> T.getVideoDimensionsFromUri(uri: Uri): Pair<Int, Int> {
    val retriever = MediaMetadataRetriever()
    try {
        retriever.setDataSource(this, uri)
        val width =
            retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toIntOrNull() ?: 0
        val height =
            retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toIntOrNull() ?: 0
        return Pair(width, height)
    } catch (e: Exception) {
        e.printStackTrace()
        return Pair(0, 0)
    } finally {
        retriever.release()
    }
}

/**
 * 将字节数格式化为更易读的字符串
 * @param bytes 字节数
 * @return 格式化后的字符串
 */
fun formatBytes(bytes: Long): String {
    if (bytes <= 0) return "0 B"
    val units = arrayOf("B", "KB", "MB", "GB", "TB")
    val digitGroups = (log10(bytes.toDouble()) / log10(1024.0)).toInt()
    return String.format(
        Locale.getDefault(),
        "%.2f %s",
        bytes / 1024.0.pow(digitGroups.toDouble()),
        units[digitGroups]
    )
}

fun createFFmpegCommand(
    inputVideoPath: String,
    targetFps: Int,
    targetWidth: Int,
    targetHeight: Int,
    args: String,
    outputGifPath: String,
) = "-i \"$inputVideoPath\"" +
        " -vf \"fps=$targetFps,scale=${targetWidth}:${targetHeight}:${args}\"" +
        " -loop 0" +
        " -y" +
        " \"$outputGifPath\""

/**
 * 将视频转换为GIF
 * @param inputUri 输入视频Uri
 * @param targetFps 目标帧率
 * @param targetWidth 目标宽度
 * @param targetHeight 目标高度
 * @param originalVideoDurationMs 原始视频时长
 * @param useCustomCommand 是否使用自定义命令
 * @param customFFmpegCommandText 自定义FFmpeg命令文本
 * @param onProgress 进度回调
 * @return 是否成功
 */
suspend fun <T : Context> T.convertVideoToGif(
    inputUri: Uri,
    targetFps: Int,
    targetWidth: Int,
    targetHeight: Int,
    originalVideoDurationMs: Long,
    useCustomCommand: Boolean,
    customFFmpegCommandText: String,
    onStarted: (Session) -> Unit = {},
    onProgress: (Float) -> Unit,
    onResultFileSize: (String) -> Unit
): Boolean {
    return withContext(Dispatchers.IO) {
        val inputVideoPath = getPathFromUri(inputUri)
        if (inputVideoPath == null) {
            println("Error: Could not get file path from Uri")
            onProgress(0f) // Reset progress on error
            return@withContext false
        }
        val inputFile = File(inputVideoPath)
        if (!inputFile.exists()) {
            println("Error: Input file does not exist")
            onProgress(0f) // Reset progress on error
            return@withContext false
        }

        val tempOutputGifFile = File(cacheDir, "${inputFile.nameWithoutExtension}_temp.gif")
        val tempOutputGifPath = tempOutputGifFile.absolutePath

        val ffmpegCommand = if (useCustomCommand) {
            customFFmpegCommandText
                .replace("\"input_video_path\"", "\"$inputVideoPath\"")
                .replace("\"output_gif_path\"", "\"$tempOutputGifPath\"")
        } else {
            createFFmpegCommand(
                inputVideoPath,
                targetFps,
                targetWidth,
                targetHeight,
                GifConstants.DEFAULT_LENS,
                tempOutputGifPath
            )
        }

        FFmpegKitConfig.enableLogCallback { log ->
            // You might need to parse FFmpeg logs to estimate progress
            // This is a simplified example and may require more sophisticated parsing
            val logMessage = log.message
            if (logMessage.contains("frame=")) {
                try {
                    val frameMatch = "frame=\\s*(\\d+)".toRegex().find(logMessage)
                    val currentFrame = frameMatch?.groupValues?.get(1)?.toInt() ?: 0

                    val durationMatch =
                        "Duration: (\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{2})".toRegex().find(logMessage)
                    val totalDurationMs = if (durationMatch != null) {
                        val hours = durationMatch.groupValues[1].toInt()
                        val minutes = durationMatch.groupValues[2].toInt()
                        val seconds = durationMatch.groupValues[3].toInt()
                        val milliseconds =
                            durationMatch.groupValues[4].toInt() * 10 // FFmpeg duration has hundredths of a second
                        (hours * 3600 + minutes * 60 + seconds) * 1000L + milliseconds
                    } else {
                        originalVideoDurationMs // Use the duration obtained earlier if log parsing fails
                    }


                    val totalFramesEstimate = (totalDurationMs / 1000.0 * targetFps).toInt()

                    if (totalFramesEstimate > 0 && currentFrame > 0) {
                        val progress =
                            min(
                                currentFrame.toFloat() / totalFramesEstimate,
                                1.0f
                            ) // Ensure progress doesn't exceed 1
                        onProgress(progress)
                    }

                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }

        val session = FFmpegKit.execute(ffmpegCommand)
        onStarted(session)
        val returnCode = session.returnCode
        val success = ReturnCode.isSuccess(returnCode)

        FFmpegKitConfig.enableLogCallback(null)

        if (success) {
            println("Video to GIF conversion successful to temp file: $tempOutputGifPath")
            val result = saveGifToPublicDirectory(tempOutputGifFile, GifConstants.DEFAULT_OUTPUT_DIR)
            val size = tempOutputGifFile.length()
            onResultFileSize(formatBytes(size))
            tempOutputGifFile.delete()
            onProgress(1.0f) // Set progress to 100% on success
            return@withContext result
        } else {
            println("Video to GIF conversion failed. Return code: $returnCode")
            println("Error message: ${session.failStackTrace}")
            tempOutputGifFile.delete()
            onProgress(0f) // Reset progress on failure
            return@withContext false
        }
    }
}

/**
 * 停止FFmpeg会话
 */
fun stopFFmpegSession(sessionId: Long = 0) {
    FFmpegKit.cancel(sessionId)
}

/**
 * 将GIF保存到公共目录
 * @param tempFile 临时GIF文件
 * @param subDirectory 子目录
 * @return 是否成功
 */
suspend fun <T : Context> T.saveGifToPublicDirectory(tempFile: File, subDirectory: String): Boolean {
    return withContext(Dispatchers.IO) {
        val resolver = contentResolver
        val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        } else {
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        }

        val fileName = tempFile.name
        val mimeType = "image/gif"

        val contentValues = ContentValues().apply {
            put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
            put(MediaStore.Images.Media.MIME_TYPE, mimeType)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                // 对于 Android Q 及以上，使用 RELATIVE_PATH
                put(
                    MediaStore.Images.Media.RELATIVE_PATH,
                    Environment.DIRECTORY_PICTURES + File.separator + subDirectory
                )
                put(MediaStore.Images.Media.IS_PENDING, 1) // 标记为待处理，稍后设置为 0
            } else {
                // 对于旧版本，需要构造绝对路径
                val picturesDir =
                    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
                val appDir = File(picturesDir, subDirectory)
                if (!appDir.exists()) {
                    appDir.mkdirs() // 创建子目录
                }
                val outputFile = File(appDir, fileName)
                put(MediaStore.Images.Media.DATA, outputFile.absolutePath)
            }
        }

        try {
            val uri = resolver.insert(collection, contentValues)

            if (uri == null) {
                println("Error creating MediaStore entry")
                return@withContext false // 如果 uri 为 null，直接返回 false
            }

            // 如果 uri 不为 null，继续处理
            resolver.openOutputStream(uri).use { outputStream ->
                if (outputStream != null) {
                    tempFile.inputStream().copyTo(outputStream)
                    println("Copied GIF to MediaStore Uri: $uri")
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                        contentValues.clear()
                        contentValues.put(MediaStore.Images.Media.IS_PENDING, 0)
                        resolver.update(uri, contentValues, null, null)
                    }
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                        MediaScannerConnection.scanFile(
                            this@saveGifToPublicDirectory,
                            arrayOf(tempFile.absolutePath),
                            arrayOf(mimeType)
                        ) { path, uri ->
                            println("MediaScanner scanned $path: -> uri=$uri")
                        }
                    }
                    return@withContext true // 成功写入并更新状态后返回 true
                } else {
                    println("Error opening output stream for Uri: $uri")
                    resolver.delete(uri, null, null) // 清理 MediaStore 记录
                    return@withContext false // 无法获取输出流返回 false
                }
            }

        } catch (e: Exception) {
            println("Error saving GIF to public directory: ${e.message}")
            return@withContext false // 捕获异常返回 false
        }
    }
}